<template>
	<label :class="['v-checkbox', { 'v-checkbox--disabled': isDisabled, 'v-checkbox--checked': isChecked, 'v-checkbox--indeterminate': isIndeterminate }]" @click="handleChange">
		<span class="v-checkbox-input">
			<span v-if="isChecked" class="v-checkbox-inner"></span>
		</span>
		<span class="v-checkbox-label">
			<slot>{{ item.label }}</slot>
		</span>
	</label>
</template>

<script setup lang="ts">
import { ref, watch, inject, computed } from 'vue';

interface CheckboxItem {
	label: string;
	value: string | number | IndeterminateValue;
}

interface IndeterminateValue {
	S: string | number | null;
	A: string | number | null;
	M: string | number | null;
}

const props = defineProps({
	value: {
		type: Array,
		default: () => []
	},
	item: {
		type: Object as () => CheckboxItem,
		required: true
	},
	disabled: {
		type: Boolean,
		default: false
	},
	indeterminate: {
		type: Boolean,
		default: false
	}
});

const emit = defineEmits(['update:value', 'change']);

const checkboxGroup = inject('checkboxGroup', null);

const isChecked = computed(() => {
	return checkboxGroup ? checkboxGroup.checkedValues.value.some((value: any) => deepEqual(value, props.item.value)) : props.value.some((v) => deepEqual(v, props.item.value));
});

const isDisabled = computed(() => {
	return checkboxGroup ? checkboxGroup.disabled.value || props.disabled : props.disabled;
});

const isIndeterminate = ref(props.indeterminate);

const deepEqual = (x: any, y: any): boolean => {
	if (x === y) return true;

	if (typeof x === 'object' && x !== null && typeof y === 'object' && y !== null) {
		if (Array.isArray(x)) {
			if (!Array.isArray(y) || x.length !== y.length) return false;
			return x.every((item, index) => deepEqual(item, y[index]));
		}

		const xKeys = Object.keys(x);
		const yKeys = Object.keys(y);
		if (xKeys.length !== yKeys.length) return false;

		return xKeys.every((key) => {
			return y.hasOwnProperty(key) && deepEqual(x[key], y[key]);
		});
	}

	return false;
};

watch(
	() => props.value,
	(newVal) => {
		isChecked.value = newVal.some((v) => deepEqual(v, props.item.value));
	},
	{ deep: true }
);

watch(
	() => props.indeterminate,
	(newVal) => {
		isIndeterminate.value = newVal;
	}
);

const handleChange = () => {
	if (isDisabled.value) return;

	let newValues = [...(checkboxGroup ? checkboxGroup.checkedValues.value : props.value)];
	const currentValue = props.item.value;

	if (checkboxGroup && checkboxGroup.indeterminate) {
		if (!isChecked.value && !isIndeterminate.value) {
			newValues.push(currentValue);
		} else if (isChecked.value && !isIndeterminate.value) {
			newValues = newValues.filter((v) => !deepEqual(v, currentValue));
		}
	} else {
		if (isChecked.value) {
			newValues = newValues.filter((v) => !deepEqual(v, currentValue));
		} else if (isIndeterminate.value) {
			newValues.push(currentValue);
			isIndeterminate.value = false;
		} else {
			isIndeterminate.value = true;
		}
	}

	if (checkboxGroup) {
		checkboxGroup.checkedValues.value = [...new Set(newValues)];
	} else {
		emit('update:value', [...new Set(newValues)]);
		emit('change', [...new Set(newValues)]);
	}
};
</script>

<style lang="scss" scoped>
.v-checkbox {
	display: flex;
	align-items: center;
	cursor: pointer;
	font-size: 14px;
	color: #606266;
	transition: all 0.3s;
	margin-right: 16px;

	&:last-child {
		margin-right: 0;
	}

	&--disabled {
		color: #c0c4cc;
		cursor: not-allowed;

		.v-checkbox-input {
			background-color: #f5f7fa;
			border-color: #e4e7ed;
			cursor: not-allowed;
		}
	}

	&--checked {
		.v-checkbox-input {
			border-color: #287afa !important;

			.v-checkbox-inner {
				background-color: #287afa !important;
				display: block !important;
			}
		}
	}

	&--indeterminate {
		.v-checkbox-input {
			border-color: #287afa !important;
			background-color: #f5f7fa !important;

			&::after {
				display: block;
			}
		}
	}

	.v-checkbox-input {
		display: inline-block;
		width: 16px;
		height: 16px;
		border: 1px solid #dcdfe6;
		border-radius: 2px;
		background-color: #fff;
		position: relative;
		margin-right: 8px;
		cursor: pointer;
		transition: all 0.3s;
		vertical-align: middle;

		.v-checkbox-inner {
			display: none;
			width: 12px;
			height: 12px;
			position: absolute;
			left: 50%;
			top: 50%;
			transform: translate(-50%, -50%);
		}

		&::after {
			content: '';
			display: none;
			position: absolute;
			left: 5px;
			top: 1px;
			width: 6px;
			height: 12px;
			border: solid #287afa;
			border-width: 0 2px 2px 0;
			transform: rotate(45deg);
		}
	}

	.v-checkbox-label {
		display: inline-block;
	}
}
</style>
